Apply Clusterize spacer heights via CSSOM so CSP cannot drop them - #1499
Merged
timothysmith0609 merged 1 commit intoAug 19, 2026
Merged
Conversation
Fixes the task-log flickering reported in Shopify/continuous-deployment#2208 (#gsd:48220): output streams fine, then flickers, then stops updating. `renderExtraTag` sets `tag.style.height` on a DETACHED node and returns `tag.outerHTML`. `insertToDOM` joins those strings and `html()` assigns them with `innerHTML`, so the height reaches the document as a style ATTRIBUTE parsed from markup. Under a `style-src` policy without 'unsafe-inline' the browser refuses to apply such an attribute. Shipit serves exactly that: $ curl -sI https://shipit.shopify.io/ content-security-policy: ... style-src 'self' https: The spacers therefore render at height 0. Total scroll height collapses to just the rendered cluster no matter how many rows exist, scrollTop is clamped into that range, getClusterNum() derives a different cluster from the clamped value, insertToDOM swaps in different rows, and the height changes again. Measured on Shipit's task page with the real assets under the verbatim production CSP, 10 seconds at 1 line/sec: without CSP with CSP with CSP + fix html() swaps 10 601 14 re-entrant swaps 0 145 0 repaints/sec ~8 83 ~8 scrollHeight at 800 rows 19316 4844 19316 The first visible line number goes from `236 237 238 239 ...` to `173 173 239 173 242 173 ...` - the viewport alternating between two regions of the log about eighty times a second. Below `rows_in_block` no spacers are emitted, which is why the page starts out fine and only degrades once the log grows. Note that the `only_bottom_offset_changed` fast path already uses `lastChild.style.height` on a LIVE node, which CSP permits; that asymmetry is why the failure presents as oscillation rather than a static break. CSSOM writes are not blocked by CSP, only markup-parsed style attributes and inline <style> elements. Verified under the policy above: detached node -> outerHTML -> innerHTML 0px el.style.height on an inserted node 500px <style> element injected at runtime 0px So carry the height through the markup round trip in a data attribute and apply it via CSSOM once the nodes are live. This keeps the fix inside the vendored library rather than weakening `style-src` for the whole application. Clusterize 0.16.0 has been unmaintained since 2018 and upstream still contains the same code, so there is no newer release to move to. Co-authored-by: AI (Pi/anthropic/claude-opus-5) <noreply@pi.dev> Assisted-By: devx/346d79f1-a786-419e-ae33-9ffc08a2bc88
offbrand
approved these changes
Aug 19, 2026
timothysmith0609
approved these changes
Aug 19, 2026
brianwarsing
added a commit
that referenced
this pull request
Aug 20, 2026
Documents #1499 (e308889), which fixed the long-standing task log flickering reported in Shopify/continuous-deployment#2208. Co-authored-by: AI (Pi/anthropic/claude-opus-5) <noreply@pi.dev> Assisted-By: devx/346d79f1-a786-419e-ae33-9ffc08a2bc88
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes the task-log flickering reported in
Shopify/continuous-deployment#2208:
output streams fine, then flickers, then stops updating, sometimes disappearing.
Reported 2025-10-29 against Shipit, open since 2025-11-06.
#gsd:48220
Root cause
renderExtraTagsetstag.style.heighton a detached node and returnstag.outerHTML.insertToDOMjoins those strings andhtml()assigns them withinnerHTML, so the heightreaches the document as a style attribute parsed from markup:
Under a
style-srcpolicy without'unsafe-inline', browsers refuse to apply such an attribute.Shipit serves exactly that policy:
So the spacers render at height 0. Total scroll height collapses to just the rendered cluster
regardless of how many rows exist,
scrollTopis clamped into that range,getClusterNum()derives a different cluster from the clamped value,
insertToDOMswaps in different rows, and theheight changes again.
The console fills with
Applying inline style violates the following Content Security Policy directive: "style-src 'self' https:".Measured
Shipit's task page with the real assets under the verbatim production CSP, 10 seconds at 1
line/sec:
html()swapsscrollHeightat 800 rows236 237 238 239 …173 173 239 173 242 173 …236 237 238 239 …The viewport alternates between two regions of the log roughly eighty times a second.
Two details that match the bug report precisely:
rows_in_blockno spacers are emitted, so the page starts out fine and only degradesonce the log grows — matching "start out fine, but soon begin flickering".
only_bottom_offset_changedfast path already useslastChild.style.heighton a livenode, which CSP permits. That asymmetry is why the failure presents as oscillation rather than a
static break.
The fix
CSSOM writes are not blocked by CSP — only markup-parsed style attributes and inline
<style>elements are. Verified under the policy above:
outerHTML→innerHTML(current behaviour)el.style.heighton an inserted node (CSSOM)<style>element injected at runtimeSo carry the height through the markup round trip in a data attribute and apply it via CSSOM once
the nodes are live. 15 lines, confined to the vendored library.
Alternatives considered
'unsafe-inline'tostyle_src. Works, but weakens the policy application-wide toaccommodate one 2016 vendored library, and reverts a posture that was deliberately tightened.
Grepping every engine asset, this is the only real offender.
style-srctocontent_security_policy_nonce_directives. Does not help — nonces applyto
<style>elements, not style attributes.Testing
The engine has no browser/system test harness, so this cannot be covered by the existing suite.
It was verified in a standalone rig that loads the real compiled assets, serves the verbatim
production CSP, and emulates
TasksController#tailbyte-for-byte. Detection is arequestAnimationFramesampler recording the first visible line number each frame; flicker isthat sequence moving backwards while scroll position does not.
Happy to attach the rig or a screen recording if useful.
Co-authored-by: AI (Pi/anthropic/claude-opus-5) noreply@pi.dev